<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

</body>
<script>

    // ===============================================================================
    // 运算符的扩展
    // 1.指数运算符 **
    // console.log(2 ** 3);//8
    // // 等同于
    // console.log(2 * 2 * 2);//8
    // // 从右往左计算
    // console.log(2 ** 2 ** 3);//256
    // console.log((2 ** 2) ** 3);//64
    // 赋值运算符 **=
    var a = 2;
    a **= 3;
    console.log(a);//8


    // --------------------------------------------------------------------------------
    // 2.可选链运算符 ?.
    // 可选链优点：是一个安全操作可以放心的使用
    // 缺点：是一个隐藏错误的操作，如果需要显示的暴露错误，不建议使用
    // 背景：要读取嵌套的属性，或者读取对象里的方法并执行
    // var a = {
    //     // 下面的属性都是不确定的，有可能是undefined
    //     b: {
    //         c: {
    //             d() {
    //                 return 'hi'

    //             }
    //         }
    //     }
    // }
    // // 直接调用如果其中有一个数据出现异常，代码会报错，阻塞了后面代码的执行
    // console.log(a.b.c.d());
    // // 为了避免在读取对象属性或执行方法的时候报错,通常使用短路的写法
    // console.log(a && a.b && a.b.c && a.b.c.d && a.b.c.d())//hi
    // // 三元也可以解决这个问题
    // console.log(a.b.c.d ? a.b.c.d() : 'undefined');
    // // 可选链运算符写法
    // console.log(a?.b?.c?.d?.());


    // --------------------------------------------------------------------------------
    // 3.Null链运算符（空值合并）  ??
    // 背景：在项目中经常会判断一个数据的真假，如果为假就取默认值，这里使用到了短路或||，但是0和''是有效的数据，也被当成了假值
    // console.log(0 || '默认值');//默认值
    // console.log('' || '默认值');//默认值
    // console.log(null || '默认值');//默认值
    // console.log(undefined || '默认值');//默认值
    // // // 专门针对null和undefined的异常场景，es6推出了空值合并??
    // console.log(0 ?? '默认值');//0
    // console.log('' ?? '默认值');//''
    // console.log(null ?? '默认值');//默认值
    // console.log(undefined ?? '默认值');//默认值



    // --------------------------------------------------------------------------------
    // 4.逻辑运算符
    // 或运算符  ||=
    a ||= b
    // 等同于：
    a || (a = b)




    // 与运算符   &&=
    a &&= b
    // 等同于：
    a && (a = b)




    // Null赋值运算符  ??=
    a ??= b
    // 等同于：
    a ?? (a = b)

    // ===============================================================================
    // Symbol、Set和Map、WeakSet和WeakMap
    //1. Symbol符号：
    // 基本数据类型，独一无二的数据，不存在变量冲突的情况
    // 类似于字符串的数据类型
    // var a = Symbol()
    // console.log(a);//Symbol()
    // var b = Symbol('name')
    // console.log(b);//Symbol(name)
    // var c = Symbol('name');
    // console.log(c);//Symbol(name)
    // console.log(b === c);//false
    // // 转换为字符串
    // console.log(b.toString());//Symbol(name)
    // console.log(c.toString());//Symbol(name)
    // console.log(b.toString() === c.toString());//true




    // Symbol的场景
    // var a = {
    //     name: 'jay',
    //     [Symbol.for('age')]: 18,
    // }
    // console.log(a.name);//jay
    // // 对象的属性名使用Symbol类型的值，如果要读取对象中的Symbol类型的属性值，无法直接取到，可以通过Symbol.for()方法取到相同描述的属性名对应的值
    // console.log(a.age);//undefined
    // console.log(a[Symbol('age')]);//undefined
    // // Symbol.for(描述)会先找相同的描述，如果有直接返回，没有就创建新的
    // console.log(a[Symbol.for('age')]);//18







    // --------------------------------------------------------------------------------
    // 2.Set是一个数据结构，跟数组非常像，内部的数据一定是是唯一的，没有重复的
    // 所以经常使用Set来进行数组去重
    // var a = new Set([10, 20, 30, 40, 50, 60, 70])
    // console.log(a);
    // // 内部数据不会重复
    // a.add(80)
    // a.add(30)
    // a.add(50)
    // console.log(a);
    // // 获取当前项
    // a.forEach((item) => {
    //     console.log(item);
    // })

    // 案例1：
    // var arr = [1, 2, 3, 4, 5, 2, 1, 7, 5, 2, 1, 5, 7, 3, 6, 2, 1, 5]
    // // 最简单的去重（数组中的元素是基本数据类型）
    // console.log([...new Set(arr)]);

    // 案例2：
    // 如何使用Set去重？使用JSON.stringify和JSON.parse
    // var arr = [{ name: 'jj' }, { name: 'joLin' }, { name: 'jj' }, { name: 'jay' }];
    // var a =[]
    // arr.forEach((item)=>{
    //     //  使用JSON.stringify将对象数组转换为字符串
    //     a.push(JSON.stringify(item))
    // })
    // var b =[];
    // new Set(a).forEach((item)=>{
    //     // 使用JSON.parse将字符串转换回对象数组
    //     b.push(JSON.parse(item));
    // })
    // console.log(b);









    // --------------------------------------------------------------------------------
    // Map()
    // 一种新的键值对的数据结构，和对象很像，但是键名不局限于字符串，可以是任意数据类型，功能比对象更完整更加强大
    // var map = new Map([
    //     [() => 'name', '张三']
    // ])
    // var obj={
    //     age:18,
    // }
    // console.log(map);
    // map.set(obj,20)
    // console.log(map);






    // --------------------------------------------------------------------------------
    // 4.WeakSet()和WeakMap()
    // WeekSet()
    // 不会嵌套引用（弱引用）
    // 不可遍历的
    // var obj = {
    //     name: 'jay'
    // }
    // var obj2 = {
    //     info: {
    //         age: 18
    //     }
    // }
    // var a = new Set();
    // a.add(obj)
    // a.add(obj2)
    // console.log(a);
    // var b = new WeakSet();
    // // WeekSet的成员只能是对象或Symbol值
    // // b.add(1)// Invalid value used in weak set
    // b.add(obj)
    // b.add(obj2)
    // console.log(b);

    // WeakMap()
    // 不会嵌套引用（弱引用）
    // 使用场景：富文本编辑器中的引用



    // --------------------------------------------------------------------------------
    // 小结：
    // Set：类似于数组，但是内部元素不会有重复的
    // Map：类似于对象，属性名可以是非字符串，功能更加强大
    // WeakSet和WeakMap：就是Set和Map数据结构的弱引用，目的是为了减少性能损耗






    // ===============================================================================
    // Proxy和Reflect
    // Proxy代理 
    //在目标对象中间架设一层拦截
    // 是一个构造函数
    // new Proxy（参数一，参数二）
    // 参数一：目标对象
    // 参数二：配置项
    // 返回值：代理后的对象
    // var obj = {
    //     name: 'jay',
    //     age: 18
    // }
    // const p = new Proxy(obj, {
    //     // target被代理的目标对象，这里指的是obj
    //     // key读取代理对象的属性名
    //     // receiver这里指的就是p
    //     // value读取代理对象要修改的属性值
    //     //监视对象属性的访问
    //     get(target, key, receiver) {
    //         // 这里可以做一些事情进行一些处理
    //         console.log('监视对象属性的访问');
    //         // console.log(target, key, receiver);
    //        // return target[key]
    //          //使用Reflect的方法把对象的所有操作变成函数行为
    //         // return Reflect.get(target,key)
    //     },
    //     //监视对象设置属性的过程
    //     set(target, key, value, receiver) {
    //         console.log('监视对象设置属性的过程');
    //         // console.log(target, key, value, receiver);
    //        // return target[key] = value;
    //          //使用Reflect的方法把对象的所有操作变成函数行为
    //        //return Reflect.get(target,key,value)
    //     }
    // })
    // console.log(obj.name);//jay
    // console.log(p.name);
    // console.log(p);
    // console.log(p.name = 'joLin');
    // console.log(p.name);//joLin





    // 面试题1：
    //Proxy和Object.defineProperty的异同点（vue3和vue2响应式原理的区别）
    // 1.defineProperty只能劫持对象的单个属性，Proxy劫持的是整个对象
    // 2.defineProperty对代理对象的操作只有两种getter和setter，Proxy对代理对象的操作很强大，有十三种操作




    // 面试题2：
    // 什么是Proxy对象？它有哪些应用场景？
    // 1.Proxy对象是ES6中引入的一个新特性，它用于创建一个对象的代理，从而可以拦截并自定义对象的基本操作，如属性查找、赋值、枚举、函数调用等
    // 2.应用场景：
    // 数据验证和过滤：在对对象属性进行赋值时，可以使用Proxy的set拦截器对数据进行验证和过滤，确保数据的合法性和正确性
    // 私有属性保护：通过Proxy的get和set拦截器，可以控制对对象属性的访问和修改，实现私有属性的保护
    // 日志和性能监控：可以在Proxy的拦截器中记录操作日志，或者监控性能数据，帮助开发者了解代码的执行情况






    // 面试题3：
    // 为什么vue3的响应式要把defineProperty改为Proxy？
    // 1.性能提升
    // 2.更广泛的拦截能力
    // 3.数组的响应式
    // 4.更好的兼容性
    // 5.简化代码
    // 6.更好的树摇（Tree-shaking）支持

    // --------------------------------------------------------------------------------
    // Reflect映射
    // 使用Reflect的方法把对象的所有操作变成函数行为
    // // 老写法
    // // 如果出现异常会报错
    // try {
    //     Object.defineProperty(target, property, att)
    // } catch {

    // }

    // // 新写法
    // // 如果出现异常返回false
    // if (Reflect.defineProperty(target, property, att)) {

    // } else {

    // }










</script>

</html>